/*******************************************************************/
/* @ All Rights Reserved Copyright (C) 2014 broada,Co.,Ltd.        */
/*                                                                 */
/*  NAME      = frshm_pool.c                                       */
/*  NOTE      =                                                    */
/*  DATE      = 2016/02/02 by guojq                                */
/*******************************************************************/
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <string.h>

#include "frcomm.h"
#include "frmem.h"
#include "frlog.h"
#include "frshm_pool.h"
#include "frshm_stat.h"
#include "frsem.h"

/*! @brief 共享内存中节点的私有信息  */
typedef struct __shm_private {
    int i_index;            //当前数据的位置
    int i_data_len;         //内容的大小
    int i_remain_all;       //全部内容长度(超过1个数据块长度的数据)
    int flg_write:1,        //是否写入   0:未写入 1:已写入
        flg_remain:1,       //有剩余内容
        flg_reserve:14;     //预留
} shm_private;

/*! @brief 共享内存中节点的信息  */
typedef struct __shm_pool_data {
    shm_private st_private; //数据的私有信息
    void *p_data;         //数据内容，不能直接使用，需要用SHM_DATA取
} shm_pool_data;

/*! @brief 共享内存的节点头信息  */
typedef struct __shm_pool_head {
    int i_shm_size;     //共享内存池的节点内容空间大小
    int i_pool_size;    //共享内存池的最大长度
    int i_used;         //已经有的数据个数
    int i_read;         //开始读取的位置 (index值，从0开始)
    int i_write;        //开始写入的位置 (index值，从0开始)
    short s_flag_stat;  //是否做统计(1:统计 0:不统计)
    int i_stat_index;   //统计信息存储位置
} shm_pool_head;

/* 共享内存的内容部分  */
#define SHM_DATA(x) ((unsigned char *)x+sizeof(shm_private))
/* 计算共享内存的大小  */
#define SHM_SIZE(x,y) (sizeof(shm_pool_head)+x*(sizeof(shm_private)+y))

#define shm_malloc(x) frmalloc(x)
#define shm_free(x)   frfree(x)

static void __frshm_read_add(shm_pool_head *p_st_head)
{
    if (p_st_head)
    {
        if (p_st_head->i_read + 1 >= p_st_head->i_pool_size)
            p_st_head->i_read = 0;
        else
            p_st_head->i_read++;
        p_st_head->i_used--;
    }
}

static void __frshm_write_add(shm_pool_head *p_st_head)
{
    if (p_st_head)
    {
        if (p_st_head->i_write + 1 >= p_st_head->i_pool_size)
            p_st_head->i_write = 0;
        else
            p_st_head->i_write++;
        p_st_head->i_used++;
    }
}

int frshm_pool_create(char *p_file, int i_pool_size, int i_shm_size)
{
    int i_key = 0, i_shmid = 0;
    int i = 0;
    void *p_shm = NULL;
    shm_pool_head *p_head = NULL;
    shm_pool_data *p_data = NULL;
    
    if (p_file == NULL || *p_file == '\0' || i_pool_size <= 0 || i_shm_size <= 0)
        return -1;
    
    i_key = ftok(p_file, 1);
    if (i_key < 0)
        return -1;
    
    //创建共享内存
    i_shmid = shmget((key_t)i_key, SHM_SIZE(i_pool_size, i_shm_size), IPC_CREAT|IPC_EXCL);
    if(i_shmid == -1)
        return -1;
    
    //取得头节点
    p_shm = shmat(i_shmid, NULL, 0);
    if (p_shm == (void*)-1)
    {
        shmctl(i_shmid, IPC_RMID, 0);
        return -1;
    }
    
    p_head = (shm_pool_head *)p_shm;
    
    p_head->i_used = 0;
    p_head->i_pool_size = i_pool_size;
    p_head->i_shm_size = i_shm_size;
    p_head->s_flag_stat = FRSHM_STAT_CHK;
    
    p_head->i_read = 0;
    p_head->i_write = 0;
    
    for (i=0;i<i_pool_size;i++)
    {
        p_data = (shm_pool_data *)((unsigned char *)p_shm + SHM_SIZE(i, i_shm_size));
        p_data->st_private.i_index = i;
        p_data->st_private.flg_write = 0;
        p_data->st_private.flg_remain = 0;
        p_data->st_private.i_data_len = 0;
        p_data->st_private.i_remain_all = 0;
        p_data->p_data = SHM_DATA(p_data);
    }
    
    if (p_head->s_flag_stat)
    {
        //添加新的共享内存统计信息
        if (frshm_stat_new(i_shmid, p_file, i_pool_size, i_shm_size
                        , &p_head->i_stat_index) != FR_SUCC)
            FRLOG_ERROR_DETAIL("frshm_stat_new error");
    }
    
    shmdt(p_shm);
    
    return i_shmid;
}

int frshm_pool_get(char *p_file)
{
    int i_key = 0;
    
    if (p_file == NULL || *p_file == '\0')
        return -1;
    
    i_key = ftok(p_file, 1);
    if (i_key < 0)
        return -1;
        
    return shmget((key_t)i_key, 0, 0);
}

int frshm_pool_write(int i_shmid, void *p_data, int i_data_len)
{
    void *p_shm = NULL;
    shm_pool_head *p_st_shm_head = NULL;
    shm_pool_data *p_st_shm_data = NULL;
    int i_write = i_data_len;
    
    if (i_shmid <0 || p_data == NULL || i_data_len <= 0)
        return -1;
    
    //将共享内存连接到当前进程的地址空间
    p_shm = shmat(i_shmid, NULL, 0);
    if(p_shm == (void*)-1)
        return -1;
    
    //设置共享内存
    p_st_shm_head = (shm_pool_head *)p_shm;
    
    //检查共享内存的桶个数
    if (p_st_shm_head->i_used >= p_st_shm_head->i_pool_size)
    {
        shmdt(p_shm);
        FRLOG_ERROR_DETAIL("share memory over flow");
        return 0;
    }
    p_st_shm_data = (shm_pool_data *)((unsigned char *)p_shm + SHM_SIZE(p_st_shm_head->i_write, p_st_shm_head->i_shm_size));

    //结构错误？
    if (p_st_shm_data->st_private.flg_write)
    {
        shmdt(p_shm);
        FRLOG_ERROR_DETAIL("write flag err");
        return -1;
    }
    //检查共享内存的单个内存大小
    if (i_data_len > p_st_shm_head->i_shm_size)
    {
        int i_loop = 0;
        int i_chk = i_data_len/(p_st_shm_head->i_shm_size);
        
        //检查是否能将数据分块存储在共享内存中
        if (p_st_shm_head->i_used + i_chk >= p_st_shm_head->i_pool_size)
        {
            shmdt(p_shm);
            FRLOG_ERROR_DETAIL("share memory over flow");
            return 0;
        }
        p_st_shm_data->st_private.i_remain_all = i_data_len;
        
        //分块存入共享内存
        while (i_data_len > p_st_shm_head->i_shm_size)
        {
            i_data_len -= p_st_shm_head->i_shm_size;
            p_st_shm_data->st_private.flg_write = 1;
            p_st_shm_data->st_private.flg_remain = 1;
            p_st_shm_data->st_private.i_data_len = p_st_shm_head->i_shm_size;
            memcpy(SHM_DATA(p_st_shm_data), (unsigned char *)p_data + i_loop*(p_st_shm_head->i_shm_size), p_st_shm_head->i_shm_size);
            __frshm_write_add(p_st_shm_head);
            //加到统计信息
            if (p_st_shm_head->s_flag_stat)
            {
                if (frshm_stat_add(g_i_shm_stat_id, g_i_sem_stat_id
                            , p_st_shm_head->i_stat_index, p_st_shm_head->i_shm_size
                            , 1) != FR_SUCC)
                    FRLOG_ERROR_DETAIL("frshm_stat_add error");
            }
            //初始化remain_all
            if (i_loop)
                p_st_shm_data->st_private.i_remain_all = 0;
            i_loop++;
            p_st_shm_data = (shm_pool_data *)((unsigned char *)p_shm + SHM_SIZE(p_st_shm_head->i_write, p_st_shm_head->i_shm_size));
        }
        p_st_shm_data->st_private.flg_write = 1;
        p_st_shm_data->st_private.flg_remain = 0;
        p_st_shm_data->st_private.i_data_len = i_data_len;
        memcpy(SHM_DATA(p_st_shm_data), (unsigned char *)p_data + i_loop*(p_st_shm_head->i_shm_size), i_data_len);
        __frshm_write_add(p_st_shm_head);
        //加到统计信息
        if (p_st_shm_head->s_flag_stat)
        {
            if (frshm_stat_add(g_i_shm_stat_id, g_i_sem_stat_id
                        , p_st_shm_head->i_stat_index, i_data_len
                        , 0) != FR_SUCC)
                FRLOG_ERROR_DETAIL("frshm_stat_add error");
        }
        shmdt(p_shm);
        
        return i_write;
    }
    
    p_st_shm_data->st_private.i_data_len = i_data_len;
    memcpy(SHM_DATA(p_st_shm_data), (unsigned char *)p_data, i_data_len);
    //加到统计信息
    if (p_st_shm_head->s_flag_stat)
    {
        if (frshm_stat_add(g_i_shm_stat_id, g_i_sem_stat_id
                    , p_st_shm_head->i_stat_index, i_data_len
                    , 0) != FR_SUCC)
            FRLOG_ERROR_DETAIL("frshm_stat_add error");
    }
    p_st_shm_data->st_private.flg_write = 1;
    p_st_shm_data->st_private.flg_remain = 0;
    
    __frshm_write_add(p_st_shm_head);
    
    shmdt(p_shm);
    return i_data_len;
}

int frshm_pool_read(int i_shmid, void **pp_data)
{
    void *p_shm = NULL;
    shm_pool_head *p_st_shm_head = NULL;
    shm_pool_data *p_st_shm_data = NULL;
    void *p_data_tmp = NULL;
    int i_data_read = 0, i_data_total = 0;
    
    if (i_shmid <0 || pp_data == NULL)
        return -1;
    
    //将共享内存连接到当前进程的地址空间
    p_shm = shmat(i_shmid, NULL, 0);
    if(p_shm == (void*)-1)
        return -1;
    
    p_st_shm_head = (shm_pool_head *)p_shm;
    if (p_st_shm_head->i_used == 0)
    {
        //没有数据可以读取
        shmdt(p_shm);
        return 0;
    }
    
    p_st_shm_data = (shm_pool_data *)((unsigned char *)p_shm + SHM_SIZE(p_st_shm_head->i_read, p_st_shm_head->i_shm_size));
    if (p_st_shm_data->st_private.flg_write == 0)
    {
        //没有数据可以读取
        shmdt(p_shm);
        return 0;
    }
    
    if (p_st_shm_data->st_private.flg_remain)
    {
        i_data_total = p_st_shm_data->st_private.i_remain_all;
        p_data_tmp = shm_malloc(i_data_total);
        if (p_data_tmp == NULL)
        {
            FRLOG_ERROR_DETAIL("share memory malloc error");
            goto err;
        }
    }
    else
    {
        i_data_total = p_st_shm_data->st_private.i_data_len;
        p_data_tmp = shm_malloc(i_data_total);
        if (p_data_tmp == NULL)
        {
            FRLOG_ERROR_DETAIL("share memory malloc error");
            goto err;
        }
    }
        
    //数据超过1块，需要多块组合到一起读取
    while (p_st_shm_data->st_private.flg_remain)
    {
        if (i_data_total - i_data_read < p_st_shm_data->st_private.i_data_len)
        {
            //错误的数据
            FRLOG_ERROR_DETAIL("share memory data error");
            goto err;
        }
        //拷贝1块数据
        memcpy((unsigned char *)p_data_tmp + i_data_read, SHM_DATA(p_st_shm_data), p_st_shm_data->st_private.i_data_len);
        i_data_read += p_st_shm_data->st_private.i_data_len;
        p_st_shm_data->st_private.flg_write = 0;
        p_st_shm_data->st_private.flg_remain = 0;
        //加到统计信息
        if (p_st_shm_head->s_flag_stat)
        {
            if (frshm_stat_sub(g_i_shm_stat_id, g_i_sem_stat_id, p_st_shm_head->i_stat_index, p_st_shm_data->st_private.i_data_len) != FR_SUCC)
                FRLOG_ERROR_DETAIL("frshm_stat_sub error");
        }
        p_st_shm_data->st_private.i_data_len = 0;
        p_st_shm_data->st_private.i_remain_all = 0;
        __frshm_read_add(p_st_shm_head);
        p_st_shm_data = (shm_pool_data *)((unsigned char *)p_shm + SHM_SIZE(p_st_shm_head->i_read, p_st_shm_head->i_shm_size));
        if (p_st_shm_data->st_private.flg_write == 0)
        {
            //错误的数据
            FRLOG_ERROR_DETAIL("share memory data error");
            goto err;
        }
    }
    
    if (i_data_total - i_data_read < p_st_shm_data->st_private.i_data_len)
        goto err;
    memcpy((unsigned char *)p_data_tmp + i_data_read, SHM_DATA(p_st_shm_data), p_st_shm_data->st_private.i_data_len);
    i_data_read += p_st_shm_data->st_private.i_data_len;
    //加到统计信息
    if (p_st_shm_head->s_flag_stat)
    {
        if (frshm_stat_sub(g_i_shm_stat_id, g_i_sem_stat_id, p_st_shm_head->i_stat_index, p_st_shm_data->st_private.i_data_len) != FR_SUCC)
            FRLOG_ERROR_DETAIL("frshm_stat_sub error");
    }
    //初始化共享内存信息
    p_st_shm_data->st_private.flg_write = 0;
    p_st_shm_data->st_private.i_data_len = 0;
    p_st_shm_data->st_private.i_remain_all = 0;
    __frshm_read_add(p_st_shm_head);
    *pp_data = p_data_tmp;
    
    shmdt(p_shm);
    
    return i_data_read;
err:
    if (p_data_tmp)
        shm_free(p_data_tmp);
    shmdt(p_shm);
    return -1;
}

void frshm_pool_free(int i_shmid)
{
    void *p_shm = NULL;
    shm_pool_head *p_head = NULL;
    shm_pool_data *p_data = NULL;
    
    if (i_shmid <0)
        return;
        
    //取得头节点
    p_shm = shmat(i_shmid, NULL, 0);
    if (p_shm == (void*)-1)
    {
        shmctl(i_shmid, IPC_RMID, 0);
        return;
    }
    
    p_head = (shm_pool_head *)p_shm;
    
    //加到统计信息
    if (p_head->s_flag_stat)
    {
        if (frshm_stat_free(g_i_shm_stat_id, g_i_sem_stat_id, p_head->i_stat_index) != FR_SUCC)
            FRLOG_ERROR_DETAIL("frshm_stat_free error");
    }
    shmctl(i_shmid, IPC_RMID, 0);
    return;
}
